#include "logger.h"
#include "tcpclient.h"

bool CSyncTcpClient::SetSocketInfo(const char* ip, ushort port)
{
    if(NULL == ip || port <= 0) return false;
    
    if(strlen(ip) > 15 || strlen(ip) < 7) return false;

    m_socketinfo._port = port; strcpy(m_socketinfo._ip, ip);

    return true;
}

bool CSyncTcpClient::SyncWork(const char* inbuf, int inlen)
{
    int& fd = m_socketinfo._fd; m_socketinfo._recvbuf = "";

    if(!ConnectToServer()) return false;

    if(!SendMsg(inbuf, inlen))
        return false;

    bool result = RecvMsg();

    SOCKET_CLOSE(fd); fd = 0;

    return result;
}

bool CSyncTcpClient::SendMsg(const char* inbuf, int inlen)
{
    int& fd = m_socketinfo._fd;

#ifdef USE_SELECT

    fd_set ready; FD_ZERO(&ready); FD_SET(fd, &ready);

    struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 10000;
    
    if(select(fd + 1, NULL, &ready, NULL, &tv) <= 0) return false;

#else

    struct pollfd poller[1]; poller[0].fd = fd; poller[0].events = POLLOUT;

    if(poll(poller, 1, 10) <= 0) return false;

#endif

    if(Send(m_socketinfo._addrinfo, fd, inbuf, inlen) <= 0) return false;

    return true;
}

bool CSyncTcpClient::RecvMsg()
{
    int& fd = m_socketinfo._fd;

    string& recvbuf = m_socketinfo._recvbuf;

    const char* addrinfo = m_socketinfo._addrinfo;

    static const int synctcpclient_recv_timeout = 30 * 1000;

    uint curtick = get_tick_count();

    while(curtick + synctcpclient_recv_timeout >= get_tick_count())
    {
#ifdef USE_SELECT

        fd_set ready; FD_ZERO(&ready); FD_SET(fd, &ready);

        struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 10000;

        if(select(fd + 1, &ready, NULL, NULL, &tv) <= 0) continue;

        if(FD_ISSET(fd, &ready) <= 0) continue;

#else

        struct pollfd poller[1]; poller[0].fd = fd; poller[0].events = POLLIN;

        if(poll(poller, 1, 10) <= 0) continue;

        if(POLLIN != poller[0].revents & POLLIN) continue;

#endif

        char outbuf[2048] = { 0 };

        int outlen = Recv(addrinfo, fd, outbuf, 2048);
 
        if(outlen <= 0)
            return false;

        m_socketinfo._recvbuf.append(outbuf, outlen);

        int len = OnRecvMsg(recvbuf.c_str(), recvbuf.size());

        if(len < 0)
        {
            return false;
        }
        else if(len > 0)
        {
            recvbuf.erase(0, len); return true;
        }
    }

    LOG_ERROR(m_log, "[recvmsg:%s] timeout\n", m_socketinfo._addrinfo);

    return false;
}

THREAD_HANDLE(tcpclient_work_thread, param)
{
    CTcpClient* client = (CTcpClient*)param;

    client->WorkThread();

    return 0;
}

CTcpClient::CTcpClient(bool isrecon)
: m_isrun(false)
, m_isrecon(isrecon)
{
}

CTcpClient::~CTcpClient()
{
    m_isrun = false;

    sleep_s(1);

    Clean();
}

void CTcpClient::Clean()
{
    //m_workthread.Stop();

    SendBuffer* sendbuf = NULL;

    while(0 == m_sendqueue.Pop(sendbuf))
    {
        DEL_SINGLE(sendbuf);
    }

    m_socketinfo._recvbuf = "";
}

bool CTcpClient::OnSendMsg(SendBuffer* sendbuf)
{
    if(m_isrun) m_sendqueue.Push(sendbuf);

    return m_isrun;
}

bool CTcpClient::StartSocket(const char* ip, ushort port)
{
    if(m_isrun) return false;

    Clean();

    if(NULL == ip || port <= 0) return false;
    
    if(strlen(ip) > 15 || strlen(ip) < 7) return false;

    m_socketinfo._port = port; strcpy(m_socketinfo._ip, ip);

    if(!ConnectToServer()) return false;

    m_isrun = true;

    if(!m_workthread.Start(tcpclient_work_thread, this))
    {
        LOG_ERROR(m_log, "[tcpclient:%s] work thread start failed\n", m_socketinfo._addrinfo);

        return false;
    }

    return true;
}

void CTcpClient::WorkThread()
{
    while(m_isrun)
    {
        sleep_ms(1);

        if(m_isrecon)
        {
            if(m_socketinfo._fd <= 0)
            {
                if(!ConnectToServer()) continue;

                m_socketinfo._recvbuf = "";
            }

            if(!SendMsg())
                continue;

            RecvMsg();
        }
        else
        {
            if(!SendMsg()) break;

            if(!RecvMsg()) break;
        }
    }

    m_isrun = false;
}

bool CTcpClient::SendMsg()
{
    SendBuffer* sendbuf = NULL;

    if(m_sendqueue.Front(sendbuf) < 0) return true;

    int& fd = m_socketinfo._fd;

    const int inlen = sendbuf->GetSize();

    const char* inbuf = sendbuf->GetByte();

#ifdef USE_SELECT

    fd_set ready; FD_ZERO(&ready); FD_SET(fd, &ready);

    struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 10000;
    
    if(select(fd + 1, NULL, &ready, NULL, &tv) <= 0) goto tcpclient_send_end;

#else

    struct pollfd poller[1]; poller[0].fd = fd; poller[0].events = POLLOUT;

    if(poll(poller, 1, 10) <= 0) goto tcpclient_send_end;

#endif

    if(Send(m_socketinfo._addrinfo, fd, inbuf, inlen) <= 0) goto tcpclient_send_end;

    m_sendqueue.Pop(sendbuf); DEL_SINGLE(sendbuf);

    return true;

tcpclient_send_end:

    if(!m_isrecon)
    {
        LOG_ERROR(m_log, "[%s], sendmsg [%d,%s] failed\n", m_socketinfo._addrinfo, inlen, inbuf);

        if(NULL != m_callbackfunc) m_callbackfunc(m_socketinfo._addrinfo);
    }

    SOCKET_CLOSE(fd); fd = 0;

    return false;
}

bool CTcpClient::RecvMsg()
{
    int& fd = m_socketinfo._fd;

    string& recvbuf = m_socketinfo._recvbuf;

#ifdef USE_SELECT

    fd_set ready; FD_ZERO(&ready); FD_SET(fd, &ready);

    struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 10000;

    if(select(fd + 1, &ready, NULL, NULL, &tv) <= 0) return true;
    
    if(FD_ISSET(fd, &ready) <= 0) return true;

#else

    struct pollfd poller[1]; poller[0].fd = fd; poller[0].events = POLLIN;

    if(poll(poller, 1, 10) <= 0) return true;

    if(POLLIN != poller[0].revents & POLLIN) return true;

#endif

    char outbuf[2048] = { 0 };

    int outlen = Recv(m_socketinfo._addrinfo, fd, outbuf, 2048);

    if(outlen <= 0)
    {
        LOG_ERROR(m_log, "[%s], recvmsg closed\n", m_socketinfo._addrinfo);

        goto tcpclient_recv_end;
    }

    recvbuf.append(outbuf, outlen);

    do
    {
        int outlen = OnRecvMsg(recvbuf.c_str(), recvbuf.size());

        if(outlen < 0)
        {
            LOG_ERROR(m_log, "[%s], recvmsg invalid packet [%d,%s]\n", m_socketinfo._addrinfo, recvbuf.size(), recvbuf.c_str());

            goto tcpclient_recv_end;
        }
        else if(outlen > 0)
            recvbuf.erase(0, outlen);
        else
            break;
    }
    while(!recvbuf.empty());

    return true;

tcpclient_recv_end:

    if(!m_isrecon)
    {
        if(NULL != m_callbackfunc) m_callbackfunc(m_socketinfo._addrinfo);
    }

    SOCKET_CLOSE(fd); fd = 0;

    return false;
}
